package ace.cmp.data.methodname.method.name.sql.entity.resolver.impl;

import ace.cmp.data.methodname.method.name.sql.core.enums.SqlCommandTypeEnum;
import ace.cmp.data.methodname.method.name.sql.core.model.MethodNameToQueryFilter;
import ace.cmp.data.methodname.method.name.sql.core.model.MethodNameToQuerySort;
import ace.cmp.data.methodname.method.name.sql.core.resolver.MethodNameToPredicateResolver;
import ace.cmp.data.methodname.method.name.sql.core.resolver.MethodNameToSubjectResolver;
import ace.cmp.data.methodname.method.name.sql.core.resolver.impl.MethodNameToPredicateResolverImpl;
import ace.cmp.data.methodname.method.name.sql.core.resolver.impl.MethodNameToSubjectResolverImpl;
import ace.cmp.data.methodname.method.name.sql.entity.model.*;
import ace.cmp.data.methodname.method.name.sql.entity.resolver.EntityMethodNameToQueryResolver;
import ace.cmp.data.methodname.method.name.sql.entity.resolver.impl.property.*;
import ace.cmp.data.methodname.method.name.sql.entity.resolver.property.EntityMethodNameToQueryPropertyResolver;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

/**
 * @author caspar
 * @date 2023/2/18 11:17 从方法解析实体查询元数据
 */
public class EntityMethodNameToQueryResolverImpl implements EntityMethodNameToQueryResolver {
  private final MethodNameToSubjectResolver subjectResolver;
  private final MethodNameToPredicateResolver predicateResolver;
  private final Method method;
  private final EntityMapping entityMapping;
  private final List<EntityMethodNameToQueryPropertyResolver> queryPropertyResolvers;
  private SqlCommandTypeEnum sqlCommandTypeEnum;

  public EntityMethodNameToQueryResolverImpl(Method method, EntityMapping entityMapping) {
    this.method = method;
    this.entityMapping = entityMapping;
    this.subjectResolver = new MethodNameToSubjectResolverImpl(this.method.getName());
    this.predicateResolver = new MethodNameToPredicateResolverImpl(this.method.getName());
    this.queryPropertyResolvers = new ArrayList<>();
    this.sqlCommandTypeEnum = this.subjectResolver.getSqlCommandType();
    this.queryPropertyResolvers.addAll(
        Arrays.asList(
            new SelectQueryPropertyEntityResolverChain(
                this.sqlCommandTypeEnum, method, entityMapping),
            new DefaultQueryResolverImpl()));
  }

  @Override
  public Method getMethod() {
    return this.method;
  }

  @Override
  public EntityMapping getEntityMapping() {
    return this.entityMapping;
  }

  @Override
  public SqlCommandTypeEnum getSqlCommandType() {
    return this.sqlCommandTypeEnum;
  }

  @Override
  public Optional<Integer> getTopNumber() {
    return this.subjectResolver.getTopNumber();
  }

  @Override
  public Boolean getDistinct() {
    return this.subjectResolver.getDistinct();
  }

  @Override
  public List<EntityPropertyMapping> getProperties() {
    for (EntityMethodNameToQueryPropertyResolver queryPropertyResolver :
        this.queryPropertyResolvers) {
      if (queryPropertyResolver.getSupport() == false) {
        continue;
      }
      return queryPropertyResolver.getProperties();
    }
    throw new RuntimeException(
        String.format(
            "[%s] method [%s] can't resolve entity property mapping",
            this.getEntityMapping().getEntityClass().getName(), this.getMethod().getName()));
  }

  @Override
  public List<EntityMethodNameToQueryFilter> getFilters() {
    List<MethodNameToQueryFilter> queryFilters = this.predicateResolver.getFilters();
    return queryFilters.stream()
        .map(
            queryFilter -> {
              EntityPropertyMapping propertyMapping =
                  this.findEntityFilterByQueryFilterAndThrow(queryFilter.getPropertyPathMeta());
              EntityMethodNameToQueryFilter entityFilter =
                  EntityMethodNameToQueryFilter.builder()
                      .logicalOp(queryFilter.getLogicalOp())
                      .relationalOp(queryFilter.getRelationalOp())
                      .propertyMapping(propertyMapping)
                      .build();
              return entityFilter;
            })
        .collect(Collectors.toList());
  }

  @Override
  public List<EntityMethodNameToQuerySort> getSorts() {
    List<MethodNameToQuerySort> querySorts = this.predicateResolver.getSorts();
    return querySorts.stream()
        .map(
            querySort -> {
              EntityPropertyMapping propertyMapping =
                  this.findEntityFilterByQueryFilterAndThrow(querySort.getPropertyPathMeta());
              EntityMethodNameToQuerySort entityFilter =
                  EntityMethodNameToQuerySort.builder()
                      .asc(querySort.getAsc())
                      .propertyMapping(propertyMapping)
                      .build();
              return entityFilter;
            })
        .collect(Collectors.toList());
  }

  private EntityPropertyMapping findEntityFilterByQueryFilterAndThrow(String propertyPathMeta) {
    Optional<EntityPropertyMapping> result =
        this.getEntityMapping().getPropertyMappingByPropertyPathMeta(propertyPathMeta);

    if (result.isEmpty()) {
      throw new RuntimeException(
          String.format(
              "[%s] method [%s] property path meta [%s] can't find entity property mapping",
              this.getEntityMapping().getEntityClass().getName(),
              this.getMethod().getName(),
              propertyPathMeta));
    }
    return result.get();
  }
}
